#include "parser.h"

Parser::Parser() = default;
Parser::Parser(std::vector<Token> tokens) :tokens(tokens) {};

double Parser::parse() {
	double ans = parseTotal();
	if (!isEnd()) {
		error("Unexcepted tail");
	}
	return ans;
}

double Parser::parseTotal() {
	double ans = parsePrefixGroup();
	switch (advance().type) {
	case TokenType::SPACE:
		ans += parseTotal();
		break;
	case TokenType::RIGHTPARENTH:
		backward();
	case TokenType::ENDOFFILE: 
		break;
	default:
		error("Unexcepted tail");
	}
	return ans;
}

double Parser::parsePrefixGroup() {
	double ans = 1;
	if (peek().type == TokenType::NUMBER) {
		ans *= parsePrefix();
	}
	ans *= parseGroup();
	return ans;
}

double Parser::parseGroup() {
	double ans = 0;
	ans += parseAtom();
	if (peek().type == TokenType::NUMBER) {
		ans *= parseSubfix();
	}
	if (peek().type != TokenType::SPACE && peek().type!=TokenType::ENDOFFILE && peek().type!= TokenType::RIGHTPARENTH) {
		ans += parseGroup();
	}
	return ans;
}

double Parser::parseAtom() {
	Token token = advance();
	double ans = 0;
	switch (token.type) {
	case TokenType::LEFTPARENTH:
		ans = parseTotal();
		if (advance().type != TokenType::RIGHTPARENTH) {
			error("Except )");
		}

		if (peek().type == TokenType::NUMBER) {
			ans *= parseSubfix();
		}
		break;
	case TokenType::ELEMENT:
		backward();
		ans = parseElement();
	}
	return ans;
}

double Parser::parsePrefix() {
	return std::stod(advance().value);
}

double Parser::parseSubfix() {
	return std::stod(advance().value);
}

double Parser::parseElement() {
	double ans = 0;
	try {
		ans = getMw(advance().value);
	}
	catch (std::exception e) {
		error(e.what());
	}
	return ans;
}

void Parser::error(std::string info) {
	throw std::exception((info + peek().value).c_str());
}

bool Parser::isEnd() {
	return position >= tokens.size() || tokens[position].type == TokenType::ENDOFFILE;
}

Token Parser::advance() {
	if (isEnd()) {
		++position;
		return Token(TokenType::ENDOFFILE, "\0");
	}
	return tokens[position++];
}

void Parser::backward() {
	if (position > 0) {
		--position;
	}
}

Token Parser::peek() {
	if (isEnd()) {
		return Token(TokenType::ENDOFFILE, "\0");
	}
	return tokens[position];
}